package io.gitee.caoxiaoyu97.log4j.defender.message.log;

import io.gitee.caoxiaoyu97.log4j.defender.constants.DefenderConstants;
import io.gitee.caoxiaoyu97.log4j.defender.properties.DefenderKeyProperties;

import java.util.Set;

public class StringFieldDefender extends AbstractLogDefender<String> {
    public StringFieldDefender(DefenderKeyProperties properties) {
        super(properties);
    }


    protected Set<String> transformToMatcher(DefenderKeyProperties properties) {
        return readKeysFromProperty(properties.getKeys(), properties.getSplitter());
    }


    protected StringBuilder doDesensitize(StringBuilder inputOrigin) {
        StringBuilder input = inputOrigin;
        int inputLen = input.length();
        for (String key : this.toMatchKeys) {
            int currStartIdx;
            for (currStartIdx = 0; currStartIdx < inputLen; ) {

                int keyStartIdx = input.indexOf(key, currStartIdx);
                if (keyStartIdx == -1) {
                    break;
                }

                boolean notValid = notValidStartKey(input, keyStartIdx);
                if (notValid) {
                    currStartIdx = keyStartIdx + key.length();

                    continue;
                }

                int commaIdx = getEndIdxWithSign(input, inputLen, keyStartIdx, ",");
                String keyValueStr = input.substring(keyStartIdx, commaIdx);


                String value = getValue(keyValueStr, key.length());
                if (null != value) {
                    currStartIdx = keyStartIdx + key.length();
                    input = getMarkerValue(input, value, keyValueStr, key, keyStartIdx);
                    inputLen = input.length();

                    continue;
                }

                commaIdx = getEndIdxWithSign(input, inputLen, keyStartIdx, "}");
                keyValueStr = input.substring(keyStartIdx, commaIdx);

                value = getValue(keyValueStr, key.length());
                if (null != value) {
                    currStartIdx = keyStartIdx + key.length();
                    input = getMarkerValue(input, value, keyValueStr, key, keyStartIdx);
                    inputLen = input.length();

                    continue;
                }
                currStartIdx = keyStartIdx + key.length();
            }
        }
        return input;
    }

    private int getEndIdxWithSign(StringBuilder input, int inputLen, int keyStartIdx, String sign) {
        int commaIdx = input.indexOf(sign, keyStartIdx + 1);
        return (commaIdx == -1) ? inputLen : commaIdx;
    }


    protected StringBuilder getMarkerValue(StringBuilder input, String value, String keyValueStr, String key, int keyStartIdx) {
        int valueStartIdx = keyValueStr.indexOf(value, key.length());
        int tmpValueStartIdx = keyStartIdx + valueStartIdx;
        return input.replace(tmpValueStartIdx, tmpValueStartIdx + value.length(), this.strategy.doMark(value));
    }

    private boolean notValidStartKey(StringBuilder input, int keyStartIdx) {
        return (keyStartIdx != 0 &&
                !DefenderConstants.PRE_DELIMITER_PATTERN.matcher(input.charAt(keyStartIdx - 1) + "").matches());
    }


    protected boolean isQuickMatch(StringBuilder input) {
        return this.toMatchKeys.stream().anyMatch(it -> (input.indexOf(it) >= 0));
    }

    protected String getValue(String keyAndValueStr, int keyLength) {
        if (keyAndValueStr.length() > keyLength) {
            String value = getValueWithSign(keyAndValueStr, keyLength, new String[]{"->", "="});
            return (null == value) ? getJsonValue(keyAndValueStr, keyLength) : value;
        }
        return null;
    }


    protected String getJsonValue(String keyAndValueStr, int keyLength) {
        int colonIdx = keyAndValueStr.indexOf(":");
        if (colonIdx == -1) {
            return null;
        }

        boolean hasQuote = ('"' == keyAndValueStr.charAt(keyLength));
        boolean hasDoubleQuote = false;

        if (!hasQuote) {
            String tmpStr = keyAndValueStr.substring(keyLength, colonIdx).trim();
            if ("\\\"".equals(tmpStr)) {
                hasDoubleQuote = true;
            }
        }
        int length = hasQuote ? 1 : (hasDoubleQuote ? 2 : 0);

        if (length > 0) {


            boolean isValid = (colonIdx < keyLength + length) ? true : isValidSplitter(keyAndValueStr.substring(keyLength + length, colonIdx));
            if (isValid) {

                String valueStr = keyAndValueStr.substring(colonIdx + 1);
                int valueStrLength = valueStr.length();

                boolean isStringValue = hasDoubleQuote ? valueStr.startsWith("\\\"") : valueStr.startsWith("\"");
                int substringStartIndex = isStringValue ? (hasDoubleQuote ? 2 : 1) : 0;


                int substringEndIndex = isStringValue ? (hasDoubleQuote ? valueStr.lastIndexOf("\\\"") : valueStr.lastIndexOf("\"")) : valueStrLength;
                String extractValueStr = valueStr.substring(substringStartIndex, substringEndIndex);
                if (DefenderConstants.INVALID_VALUE_PATTERN.matcher(extractValueStr).matches()) {
                    return null;
                }
                return extractValueStr;
            }
        }

        return null;
    }


    private String getValueWithSign(String keyAndValueStr, int keyLength, String... equalSigns) {
        for (String equalSign : equalSigns) {
            int signIdx = keyAndValueStr.indexOf(equalSign);
            if (signIdx != -1) {
                if (isValidSplitter(keyAndValueStr.substring(keyLength, signIdx))) {
                    return keyAndValueStr.substring(signIdx + equalSign.length());
                }
            }
        }
        return null;
    }

    private boolean isValidSplitter(String input) {
        return DefenderConstants.VALID_CONCAT_PATTERN.matcher(input).matches();
    }
}
